﻿using Zore.App.Shared.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Zore.App.Shared.Helpers.Invoker
{
    public abstract class InvokerContributor
    {
        public Uri BaseAddress { get; protected set; }

        public InvokerContributor(string baseAddress)
        {
            if (baseAddress.IsNullOrWhiteSpace())
            {
                throw new Exception(
                    "API Invoker: baseAddress cannot be null or empty.");
            }
            this.BaseAddress = new Uri(baseAddress.EnsureTrailingSlash());
        }

        public async Task<TReturn> InvokeWithReturn<TReturn, TData>(
            string requestUri, HttpMethod method, TData data) where TData : class
        {
            return await InnerInvokeWithReturn<TReturn, TData>(requestUri, method, data);
        }

        public async Task<TReturn> InvokeWithReturn<TReturn>(string requestUri, HttpMethod method)
        {
            return await InnerInvokeWithReturn<TReturn, Object>(requestUri, method, null);
        }

        public Task InvokeWithoutReturn<TData>(
            string requestUri, HttpMethod method, TData data) where TData : class
        {
            return InnerInvokeWithoutReturn<TData>(requestUri, method, data);
        }

        public Task InvokeWithoutReturn(string requestUri, HttpMethod method)
        {
            return InnerInvokeWithoutReturn<Object>(requestUri, method, null);
        }

        public virtual async Task<TReturn> InnerInvokeWithReturn<TReturn, TData>(
            string requestUri, HttpMethod method, TData data = null) where TData : class
        {
            var result = default(TReturn);
            using (var client = new HttpClient())
            {
                using (var request = BuildHttpRequestMessage(requestUri, method, data))
                {
                    try
                    {
                        client.BaseAddress = BaseAddress;
                        var msg = await client.SendAsync(request);
                        if (msg.StatusCode == HttpStatusCode.OK)
                        {
                            var strResult = await msg.Content.ReadAsStringAsync();
                            result = JsonConvert.DeserializeObject<TReturn>(strResult);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(
                            $"Error:{ex.Message} Inner Error:{ex.InnerException.Message}");
                    }
                    return result;
                }
            }
        }


        public virtual async Task<byte[]> InnerInvokeReturnByte<TData>(
          string requestUri, HttpMethod method, TData data = null) where TData : class
        {
            byte[] result = null;
            using (var client = new HttpClient())
            {
                using (var request = BuildHttpRequestMessage(requestUri, method, data))
                {
                    try
                    {
                        client.BaseAddress = BaseAddress;
                        var msg = await client.SendAsync(request);
                        if (msg.StatusCode == HttpStatusCode.OK)
                        {
                            result = await msg.Content.ReadAsByteArrayAsync();
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(
                            $"Error:{ex.Message} Inner Error:{ex.InnerException.Message}");
                    }
                    return result;
                }
            }
        }

        public virtual async Task InnerInvokeWithoutReturn<TData>(
            string requestUri, HttpMethod method, TData data = null) where TData : class
        {
            using (var client = new HttpClient())
            {
                using (var request = BuildHttpRequestMessage(requestUri, method, data))
                {
                    client.BaseAddress = BaseAddress;
                    var msg = await client.SendAsync(request);
                }
            }
        }

        public virtual HttpRequestMessage BuildHttpRequestMessage<TData>(
            string requestUri, HttpMethod method, TData data = null) where TData : class
        {
            var strData = string.Empty;
            if (data != null)
            {
                strData = JsonConvert.SerializeObject(data, new JsonSerializerSettings
                {
                    ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                });
            }

            var request = new HttpRequestMessage(method, requestUri);
            if (!strData.IsNullOrWhiteSpace())
            {
                var content = new StringContent(strData, Encoding.UTF8);
                request.Content = content;
                request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            }
            return request;
        }
    }
}
